home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / System 7.0 Samples / Edition Manager / TextSections.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-12  |  21.0 KB  |  497 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2.  *
  3.  *  Apple Developer Technical Support
  4.  *
  5.  *  Text section handling routines
  6.  *
  7.  *  Program:    EditionSample
  8.  *  File:       TextSections.c -    C Source
  9.  *
  10.  *  by:         C.K. Haun <TR>
  11.  *
  12.  *  Copyright © 1990,1991 Apple Computer, Inc.
  13.  *  All rights reserved.
  14.  *
  15.  *------------------------------------------------------------------------------
  16.  * This file contains the functions for manipulating and bookkeeping
  17.  * for published and subscribed text sections in this sample.
  18.  *----------------------------------------------------------------------------*/
  19.  
  20. #define __TEXTSECTIONS__
  21.  
  22. #pragma segment TextHandler
  23. #pragma load "EdSampheaders"                                /* see the Buildheaders.c file */
  24.  
  25. #include "EdSampdefines.h"
  26.  
  27. /* prototypes */
  28. Boolean HasTESelection(windowCHandle inWind);
  29. Boolean PasteText(void);
  30. Boolean CutText(void);
  31. Boolean CopyText(void);
  32. Boolean ClearText(void);
  33. void BorderTextSection(textSectionHandle theText);
  34. void CheckTextSections(windowCHandle inWindow, short Action);
  35. Boolean SkipOverSubscriber(windowCHandle inWindow, unsigned short theKey);
  36. void RePackText(textSectionHandle currentSection, TEHandle theTEHandle);
  37. void CheckSectionHit();
  38. static Boolean Touching(textSectionHandle checkSection, TEHandle teText);
  39. /* externals */
  40. extern WindowPtr gCurrentWindow;
  41. extern short gClipHasContents;
  42. extern Boolean gShowingAll;
  43. extern WindowPtr FindSection(SectionHandle inSection);
  44. extern SectionHandle gShowingSecHandle;
  45. extern Boolean gShowPub,gShowSub;
  46. extern Rect        gShowSubRect,gShowPubRect;
  47. extern void MyHiHook(void);
  48. Rect nulRect={0,0,0,0};
  49. /* the following few functions are for text, and will be expanded as this is fully implemented */
  50. Boolean HasTESelection(windowCHandle inWind)
  51. {
  52.     TEHandle temp;
  53.     if (inWind == nil)
  54.         return(false);
  55.     temp = (*inWind)->boxHandle;
  56.     if (temp != nil)
  57.         return((*temp)->selStart != (*temp)->selEnd);
  58.     else
  59.         return(false);
  60. }
  61.  
  62. Boolean PasteText(void)
  63. {
  64.     windowCHandle tempWC;
  65.     tempWC = (windowCHandle)GetWRefCon(gCurrentWindow);
  66.     if ((*tempWC)->boxHandle != nil && gClipHasContents == kClipHasText) {
  67.         TEFromScrap();
  68.         CheckTextSections(tempWC, kAdding);
  69.         TEPaste((*tempWC)->boxHandle);
  70.         return(true);
  71.     }
  72.     return(false);
  73. }
  74.  
  75. Boolean CutText(void)
  76. {
  77.     windowCHandle tempWC;
  78.     tempWC = (windowCHandle)GetWRefCon(gCurrentWindow);
  79.     if (HasTESelection(tempWC)) {
  80.         CheckTextSections(tempWC, kRemoving);               /* adjusts the end points on text sections */
  81.         TECut((*tempWC)->boxHandle);
  82.         ZeroScrap();
  83.         TEToScrap();
  84.         return(true);
  85.     }
  86.     return(false);
  87. }
  88.  
  89. Boolean CopyText(void)
  90. {
  91.     windowCHandle tempWC;
  92.     tempWC = (windowCHandle)GetWRefCon(gCurrentWindow);
  93.     if (HasTESelection(tempWC)) {
  94.         TECopy((*tempWC)->boxHandle);
  95.         ZeroScrap();
  96.         TEToScrap();
  97.         return(true);
  98.     }
  99.     return(false);
  100. }
  101.  
  102. Boolean ClearText(void)
  103. {
  104.     windowCHandle tempWC;
  105.     tempWC = (windowCHandle)GetWRefCon(gCurrentWindow);
  106.     if (HasTESelection(tempWC)) {
  107.         CheckTextSections(tempWC, kRemoving);
  108.         TEDelete((*tempWC)->boxHandle);
  109.         return(true);
  110.     }
  111.     return(false);
  112. }
  113.  
  114. /* This checks all the publishers and subscribers in the current TE record, and sees */
  115. /* if their data needs to be modified based on the TE action pending */
  116. void CheckTextSections(windowCHandle inWindow, short Action)
  117. {
  118.     TEHandle theTEHandle = (*inWindow)->boxHandle;
  119.     textSectionHandle startSection = (*inWindow)->textSections;
  120.     Boolean testFlag = true;                                /* a gen purp flag please */
  121.     Boolean whapBorder = false;
  122.     textSectionHandle currentSection = startSection;
  123.     short counter = 0;
  124.     /*  see if the insertion point or selection range is after all our sections.  */
  125.     /* if it is (a forlorn hope) we can exit without adjusting anything. */
  126.     if (startSection == nil)
  127.         return;                                             /* no sections, go away */
  128.     do {
  129.         if ((*currentSection)->endChar > (*theTEHandle)->selStart) {
  130.             testFlag = false;
  131.             counter++;                                      /* a minimal aid to exiting this stuff later */
  132.         if((*currentSection)->bordered)whapBorder=true;
  133.         }
  134.         currentSection = (*currentSection)->nextSection;
  135.     } while (currentSection);
  136.     if (testFlag)
  137.         return;                                             /* no section is affected by this action, go away */
  138.     
  139.     /* OK, someone (or two, or twenty) will be affected by this action */
  140.     /* so we have to see who, and how much, and if it's like adding in the middle, */
  141.     /* or like shifting start and end points down, or like whatever, y'know? */
  142.     
  143.     currentSection = (*inWindow)->textSections;
  144.     switch (Action) {
  145.         unsigned long actionLen;
  146.         case kKeyStroke:
  147.             /* so we bump the start and end positions of all the sections after this point by 1 */
  148.             while (counter) {                               /* minimal aid in exiting this thing */
  149.                 if ((*theTEHandle)->selStart < (*currentSection)->startChar) {
  150.                     (*currentSection)->startChar++;
  151.                     (*currentSection)->endChar++;
  152.                     counter--;
  153.                 } else {
  154.                     if ((*theTEHandle)->selStart > (*currentSection)->startChar &&
  155.                         (*theTEHandle)->selStart < (*currentSection)->endChar) {
  156.                         /* this should mean that the input char is between the start and the end */
  157.                         /* so the publisher expands by 1 */
  158.                         (*currentSection)->endChar++;
  159.                         counter--;
  160.                     }
  161.                 }
  162.                 currentSection = (*currentSection)->nextSection;
  163.                 if (currentSection == nil)
  164.                     break;                                  /* hey, y'know? */
  165.             }
  166.             break;
  167.         case kAdding:
  168.             /* increase start and finished positions by the size of the clip being added */
  169.             actionLen = TEGetScrapLen();
  170.             /* so we bump the start and end positions of all the sections after this point by 1 */
  171.             while (counter) {                               /* minimal aid in exiting this thing */
  172.                 if ((*currentSection)->startChar > (*theTEHandle)->selStart) {
  173.                     (*currentSection)->startChar += actionLen;
  174.                     (*currentSection)->endChar += actionLen;
  175.                     counter--;
  176.                 } else {Boolean temp=false;
  177.                     /* expand the published section */
  178.                   temp=(*currentSection)->bordered;
  179.                     (*currentSection)->endChar += actionLen;
  180.                     
  181.                 }
  182.                 currentSection = (*currentSection)->nextSection;
  183.                 if (currentSection == nil)
  184.                     break;                                  /* hey, y'know? */
  185.             }
  186.             break;
  187.         case kRemoving:
  188.             /* decrease start and finished positions by the size of the current selection */
  189.             /* You have another worry in this area, of course.  If the user has highlighted */
  190.             /* the WHOLE publisher or subscriber, that means that you will be deleting */
  191.             /* it completely */
  192.             actionLen = (*currentSection)->endChar - (*currentSection)->startChar;
  193.             /* so we bump the start and end positions of all the sections after this point by 1 */
  194.             while (counter) {                               /* minimal aid in exiting this thing */
  195.                 if ((*currentSection)->startChar > (*theTEHandle)->selStart) {
  196.                     (*currentSection)->startChar -= actionLen;
  197.                     (*currentSection)->endChar -= actionLen;
  198.                     counter--;
  199.                 }
  200.                 currentSection = (*currentSection)->nextSection;
  201.                 if (currentSection == nil)
  202.                     break;                              
  203.             }
  204.             break;
  205.     }
  206.     /* if anything changed and borders are on in any section, we need to */
  207.     /* erase and redraw the borders */
  208.     /* This will cause a lot of screen flicker, you can do it neater in your app */
  209.     if(whapBorder)InvalRect(&(*theTEHandle)->viewRect);
  210. }
  211.  
  212. /* SkipOverSubscriber is used to move the insertion point around any subscriptions */
  213. /* in response to a cursor or delete key action */
  214. /* This is, of course, because you cannot edit the contents of a subscription */
  215. Boolean SkipOverSubscriber(windowCHandle inWindow, unsigned short theKey)
  216. {
  217.     long oldStart, oldEnd;
  218.     Boolean flagBack = false;
  219.     short newInsertionPoint;                                /* where the insertion point will be moving to as */
  220.     /*  a result of this key */
  221.     /* first, as usually, see if this action impinges on the target */
  222.     /* of course, this only counts for subscriptions */
  223.     TEHandle theTEHandle = (*inWindow)->boxHandle;
  224.     textSectionHandle startSection = (*inWindow)->textSections;
  225.     if (!startSection)
  226.         return(flagBack);                                   /* scat if no sections */
  227.     do {
  228.         /* first see if it's a publisher, if it is, skip it.  */
  229.         if (!(*startSection)->publishing) {
  230.             switch (theKey) {                               /* actions based on what direction this key is moving us */
  231.                 case kLeftArrow:
  232.                     newInsertionPoint = ((*theTEHandle)->selStart) - 1;
  233.                     if (newInsertionPoint <= (*startSection)->endChar && newInsertionPoint >= (*startSection)->startChar) {
  234.                         flagBack = true;
  235.                         TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle);
  236.                     }
  237.                     break;
  238.                     
  239.                 case kRightArrow:
  240.                     newInsertionPoint = ((*theTEHandle)->selEnd) + 1;
  241.                     if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) {
  242.                         flagBack = true;
  243.                         TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle);
  244.                     }
  245.                     break;
  246.                 case kUpArrow:
  247.                 case kDownArrow:
  248.                     /* for up and down arrows, we actually have to do the TEKey thing here, then evaluate the */
  249.                     /* result and see if it landed in a subscription.  This is of course because we're using */
  250.                     /* TextEdit, if you're doing your own text processing you will know where any keystroke lands */
  251.                     /* Of course, you gotta save the current position of the insertion point first */
  252.                     oldStart = (*theTEHandle)->selStart;
  253.                     oldEnd = (*theTEHandle)->selEnd;
  254.                     TEKey(theKey, theTEHandle);
  255.                     /* see where it ended up */
  256.                     newInsertionPoint = (*theTEHandle)->selEnd;
  257.                     if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) {
  258.                         flagBack = true;
  259.                         /* now move before or after the section depending on the arrow */
  260.                         if (theKey == kUpArrow)
  261.                             TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle);
  262.                         else
  263.                             TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle);
  264.                     } else {
  265.                         TESetSelect(oldStart, oldEnd, theTEHandle);
  266.                     }
  267.                     break;
  268.                     
  269.             }
  270.         }
  271.         startSection = (*startSection)->nextSection;        /* go to next in list */
  272.     } while (startSection);
  273.     
  274.     return(flagBack);
  275. }
  276.  
  277. /* RePackText makes sure we have the proper text in our lil publisher before writing the */
  278. /* thing out.  */
  279. /* Actually, since this function exists, we don't really need to keep a copy of the text attached */
  280. /* to the publisher.  */
  281. void RePackText(textSectionHandle currentSection, TEHandle theTEHandle)
  282. {
  283.     short theLength;
  284.     CharsHandle theRawText;
  285.     Ptr src, dest;
  286.     theLength = (*currentSection)->endChar - (*currentSection)->startChar;
  287.     SetHandleSize((*currentSection)->theText, theLength);
  288.     HLock((*currentSection)->theText);
  289.     theRawText = TEGetText(theTEHandle);
  290.     HLock((Handle)theRawText);
  291.     src = (Ptr)*theRawText;
  292.     src = src + (*currentSection)->startChar;
  293.     BlockMove((Ptr)((Ptr)*theRawText)+(*currentSection)->startChar, (Ptr)*((*currentSection)->theText), theLength);
  294.     HUnlock((Handle)theRawText);
  295.     HUnlock((*currentSection)->theText);
  296. }
  297.  
  298. /* CheckSubscriberHit sees if the user person clicked inside a subscription.  If they did, then */
  299. /* we'll either highlight the whole subscriber and <sometime> draw a border around it, or */
  300. /* extend the selection range to include the whole subscription if they dragged into just part of it */
  301. /* for bording, things get a little more complicated.  If this is a publisher, and it's already */
  302. /* showing a border, we want to do a normal TEClick since the user can edit inside a publisher */
  303. void CheckSectionHit()
  304. {
  305.     windowCHandle shortName = (windowCHandle)GetWRefCon(FrontWindow());
  306.     TEHandle theTEHandle = (*shortName)->boxHandle;
  307.     long mySelStart = (*theTEHandle)->selStart;             /* added these variables just to make the code a */
  308.     long mySelEnd = (*theTEHandle)->selEnd;                 /* little easier to read */
  309.     long newStart = mySelStart;                             /* if any adjustments need to be made */
  310.     long newEnd = mySelEnd;
  311.     Boolean startedBordered;
  312.     textSectionHandle startSection = (*shortName)->textSections;
  313.     textSectionHandle currentSection = startSection;
  314.     /* and of course, the affected window is the front window, since they clicked there, y'know */
  315.     /* what we do here is see if the current mouse click lands in a publisher or */
  316.     /* subscriber.  If it's a publisher, check for a double click and highlite. */
  317.     /* if the selection range covers ANY part of a subscriber, extend the selection to */
  318.     /* encompass the whole subscriber.  Then border the subscriber also. */
  319.     /* This means that we cannot exit after finding one pub or sub, we must */
  320.     /* check all since the user could have selected all the text in the record */
  321.     while (currentSection) {
  322.         startedBordered = (*currentSection)->bordered;
  323.         if (Touching(currentSection, theTEHandle)) {
  324.             /* see if this is a subscriber.  If so, extend start and end positions to include it all */
  325.             if (!(*currentSection)->publishing) {
  326.                 /* it is a subscriber */
  327.                 if ((*currentSection)->startChar < mySelStart)
  328.                     newStart = (*currentSection)->startChar;
  329.                 if ((*currentSection)->endChar > mySelEnd)
  330.                     newEnd = (*currentSection)->endChar;
  331.         TESetSelect(newStart, newEnd, theTEHandle);        
  332.                 
  333.             } else {
  334.             /* for a publisher, highlith the whole thing if it's the first click.  If it's the */
  335.             /* second (or subsequent, o' course) they want to edit, so let them */
  336.      if (startedBordered)
  337.         TESetSelect(newStart, newEnd, theTEHandle);        
  338.     else
  339.         TESetSelect((*currentSection)->startChar, (*currentSection)->endChar, theTEHandle);
  340.             }
  341.         } else {
  342.         /* if this section was not hit, deborder it */
  343.         if(!gShowingAll && (*currentSection)->bordered){(*currentSection)->bordered=false;
  344.             InvalRect(&(*theTEHandle)->viewRect);
  345.         }}
  346.         currentSection = (*currentSection)->nextSection;
  347.     }
  348.     
  349.    
  350.     
  351. }
  352.  
  353. /*  Touching was created just so I wouldn't have to have a huge if statement in */
  354. /* CheckSectionHit */
  355. /* All it does is see if the current selection range of the TEHandle touches or */
  356. /* fully encloses any text sections */
  357. static Boolean Touching(textSectionHandle checkSection, TEHandle teText)
  358. {
  359.     Boolean ouch = false;                                   /* will turn true if a touch actually happened */
  360.     
  361.     long tStart = (*teText)->selStart;
  362.     long tEnd = (*teText)->selEnd;
  363.     short ourStart = (*checkSection)->startChar;
  364.     short ourEnd = (*checkSection)->endChar;
  365.     /* first see if the start hits inside the section */
  366.     if (tStart > ourStart && tStart < ourEnd)
  367.         ouch = true;
  368.     else if (tStart < ourStart && tEnd > ourStart)
  369.         ouch = true;                                        /* now see if we got completely enclosed */
  370.     else if (tEnd > ourStart && tEnd < ourEnd)
  371.         ouch = true;
  372.     /* and finally see if we got touched by the end of the thing */
  373.     if (ouch){
  374.         BorderTextSection(checkSection);
  375.         /* put this section in our gloabl section handle holder so we can do options on it */
  376.         gShowingSecHandle=(*checkSection)->theSection;
  377.         if((*checkSection)->publishing) {
  378.         gShowPub = true;
  379.         gShowPubRect=nulRect;
  380.         } else {
  381.          gShowSub = true;
  382.         gShowSubRect=nulRect;
  383.         }}
  384.     return(ouch);
  385. }
  386.  
  387. /* ExcludeSubscriber removes subscriptions from the range of selected text, so when a keystroke */
  388. /* replaces a selection, the subscriber does not also get replaced. */
  389. void ExcludeSubscriber(windowCHandle tempCH)
  390. {
  391.     
  392. }
  393.  
  394. /* this borders the selected section.  It's a pain.  */
  395. /* BUT, it's a pain because I'm using TextEdit.  If you're writing a word processor */
  396. /* or another application which does it's own text processing, this will be a lot easier */
  397. /* •••• NOTE:  this doesn't look very good when a subscriber is embedded in a publisher, since */
  398. /* the lines of course overlap.  I don't know how to deal with this yet. */
  399. void BorderTextSection(textSectionHandle theText)
  400. {
  401.     TEHandle theTEHandle;
  402.     WindowPtr theWindow;
  403.     windowCHandle shortName;
  404.     PolyHandle borderPoly;
  405.     long origIStart;
  406.     long origIEnd;
  407.     long newStart;
  408.     long newEnd;
  409.     Point startPoint;
  410.     Point endPoint;
  411.     RgnHandle oldClip = NewRgn();
  412.     WindowPtr oldPort;
  413.     GetPort(&oldPort);
  414.     GetClip(oldClip);
  415.     
  416.     theWindow = FindSection((*theText)->theSection);
  417.     SetPort(theWindow);
  418.     shortName = (windowCHandle)GetWRefCon(theWindow);
  419.     HLock((Handle)shortName);
  420.     theTEHandle = (*shortName)->boxHandle;
  421.     
  422.     origIStart = (*theTEHandle)->selStart;
  423.     origIEnd = (*theTEHandle)->selEnd;
  424.     TESetSelect((*theText)->startChar, (*theText)->endChar, theTEHandle);
  425.     newStart = (*theTEHandle)->selStart;
  426.     newEnd = (*theTEHandle)->selEnd;
  427.     ClipRect(&(*theTEHandle)->viewRect);
  428.     /* so our poly doesn't exceed the bounds of our rect */
  429.     /* now what we do is create a Polygon that's the shape of the text section */
  430.     startPoint = TEGetPoint((short)newStart, theTEHandle);
  431.     endPoint = TEGetPoint((short)newEnd, theTEHandle);
  432.     /* •••• NOTE:  This is a really simplistic check, since this is a simple sample.  */
  433.     /* In a more complex text document you'd be worrying about text scrolled out of the view rect, */
  434.     /* weird regions, and other things I'm not going to deal with here.  */
  435.     /* see if the verts are the same.  If they are, then all we need is a simple rect */
  436.     
  437.     if (startPoint.v == endPoint.v) {
  438.         borderPoly = OpenPoly();
  439.         startPoint.v -= (*theTEHandle)->lineHeight;         /* top o' the line to you! */
  440.         
  441.         MoveTo(startPoint.h, startPoint.v);
  442.         LineTo(endPoint.h, startPoint.v);
  443.         LineTo(endPoint.h, endPoint.v);
  444.         LineTo(startPoint.h, endPoint.v);
  445.         LineTo(startPoint.h, startPoint.v);
  446.         ClosePoly();
  447.         PenSize(3, 3);
  448.         if ((*theText)->publishing)
  449.             PenPat(qd.gray);
  450.         else
  451.             PenPat(qd.dkGray);
  452.         FramePoly(borderPoly);
  453.         PenPat(qd.black);
  454.         PenSize(1, 1);
  455.         
  456.     } else {
  457.         short endTop = endPoint.v -(*theTEHandle)->lineHeight;
  458.         /* need to build a slightly more complex poly */
  459.         borderPoly = OpenPoly();
  460.         startPoint.v -= (*theTEHandle)->lineHeight;         /* top o' the line to you! */
  461.         
  462.         MoveTo(startPoint.h, startPoint.v);
  463.         LineTo((*theTEHandle)->destRect.right, startPoint.v);
  464.         LineTo((*theTEHandle)->destRect.right, endTop);
  465.         LineTo(endPoint.h, endTop);                         /* may not be needed, but doesn't hurt anything */
  466.         LineTo(endPoint.h, endPoint.v);
  467.         LineTo((*theTEHandle)->destRect.left, endPoint.v);
  468.         LineTo((*theTEHandle)->destRect.left, (startPoint.v + ((*theTEHandle)->lineHeight)));
  469.         LineTo(startPoint.h, (startPoint.v + ((*theTEHandle)->lineHeight)));
  470.         LineTo(startPoint.h, startPoint.v);
  471.         ClosePoly();
  472.         PenSize(3, 3);
  473.         if ((*theText)->publishing)
  474.             PenPat(qd.gray);
  475.         else
  476.             PenPat(qd.dkGray);
  477.         FramePoly(borderPoly);
  478.         PenPat(qd.black);
  479.         PenSize(1, 1);
  480.         
  481.     }
  482.     if((*theText)->bordered)
  483.     /* if it started bordered, and it's a publisher, then when set the selection range to */
  484.     /* what it was when we entered */
  485.         TESetSelect(origIStart,origIEnd, theTEHandle);    
  486.      else 
  487.         (*theText)->bordered = true;     /* log it bordered */
  488.     KillPoly(borderPoly);
  489.     SetClip(oldClip);
  490.     DisposeRgn(oldClip);
  491.     SetPort(oldPort);
  492.     HUnlock((Handle)shortName);
  493. }
  494.  
  495.  
  496. #undef __TEXTSECTIONS__
  497.